package com.woniu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.woniu.entity.Result;
import com.woniu.mapper.*;
import com.woniu.pojo.Car;
import com.woniu.pojo.Order;
import com.woniu.pojo.Space;
import com.woniu.service.IOrderSettlementService;
import com.woniu.vo.ChargeReturnVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.sql.Time;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Service
public class OrderSettlementServiceImpl implements IOrderSettlementService {

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private OrderSettlementMapper orderSettlementMapper;
    @Resource
    private OrderServiceImpl orderService;
    @Resource
    private RoadMapper roadMapper;
    @Resource
    private SpaceMapper spaceMapper;
    @Resource
    private CarMapper carMapper;

    @Override
    public Result driveOut(long magnetId, String magnetStatus) throws ParseException {
        //封装返回对象
        Result returnMessage = new Result();
        //1. 改变泊位表状态
        int row = orderSettlementMapper.changeSpaceStatus(magnetId,magnetStatus);
        //2. 查询此泊位正在进行的订单
        Order order = orderSettlementMapper.selectOrderingByMagnetId(magnetId);
        //3. 判断有无查到订单
        if(order == null){
            return new Result(false,"该泊位无正在进行的订单！");
        }
        //4.记录驶离时间，将订单状态更改为待缴费
        Order order1 = new Order();
        order1.setId(order.getId());
        order1.setLeaveTime(new Date());
        order1.setStatus("1");
        int row2 = orderMapper.updateById(order1);
        //5.进行订单结算
        //再次获取订单 结算
        Order order2 = orderMapper.selectById(order.getId());
        BigDecimal price = settleOrder(order2);
        //6.将结算金额设置回订单
        order2.setMoney(price);
        int row3 = orderMapper.updateById(order2);
        Order order3 = orderMapper.selectById(order.getId());
        //构建返回对象
        Result result = new Result();
        result.setData(order3);
        result.setFlag(true);
        //如果订单金额为0，则直接完成订单
        if(order3.getMoney().compareTo(new BigDecimal(0)) == 0){
            Order order4 = new Order();
            order4.setStatus("3");
            order4.setId(order.getId());
            int row5 = orderMapper.updateById(order4);
            result.setMessage("订单金额为0，直接完成！");
            return result;
        }
        //7. 判断该订单有无关联用户，有则直接推送给用户，没有则推送给对应巡检员
        //查询此订单关联的用户
        Car car = carMapper.selectById(order3.getCarId());
        //判断有无关联用户
        if(car.getUserId() == 0){
            //没有关联用户
            result.setMessage("没有关联用户，已将此订单推送给巡检员进行现场收费！");
        }else {
            //有用户
            result.setMessage("已将此订单推送给用户缴费！");
        }
        return result;
    }

    /**
    * 根据订单id查询现在已经产生的费用
    * */
    @Override
    public Result currentCost(long orderId) throws ParseException {
        //1.根据id查询订单
        Order order = orderMapper.selectById(orderId);
        //2.将订单对象的驶离时间设置为当前
        order.setLeaveTime(new Date());
        //3.调用订单进行结算
        BigDecimal currentPrice = settleOrder(order);
        //4.构建返回对象
        Result result = new Result();
        result.setData(currentPrice);
        result.setFlag(true);
        result.setMessage("当前订单已产生费用查询成功！");
        return result;
    }

    /**
     * 结算订单的方法
     * */
    public BigDecimal settleOrder(Order order) throws ParseException {
        //0. 查询此订单关联的路段,获取路段id
        Space space = spaceMapper.selectById(order.getSpaceId());
        //1. 获取该订单的收费规则收费方式
        List<ChargeReturnVo> chargeReturn = orderService.searchChargeRule(space.getRoadId());
        //2. 判断订单为哪种收费方式 0全天收费，1分时段收费，2分时段禁停收费
        //定义收费金额 price1为首天收费金额，price2为末天收费金额，price3为中间天数收费金额
        BigDecimal price1 = new BigDecimal(0);
        BigDecimal price2 = new BigDecimal(0);
        BigDecimal price3 = new BigDecimal(0);
        //全天收费订单结算
        if(chargeReturn.get(0).getType().equals("0")){
            //获取车辆的驶入驶离时间
            Date driveInTime = order.getCreateTime();
            Date driveLeaveTime = order.getLeaveTime();
            //获取驶入驶离时间的时间部分
            SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
            String in = format.format(driveInTime);
            Date driveInTimeTime = format.parse(in);
            String leave = format.format(driveLeaveTime);
            Date driveLeaveTimeTime = format.parse(leave);
            //获取驶入驶出时间的Calendar类
            Calendar calDriveInTime = Calendar.getInstance();
            calDriveInTime.setTime(driveInTime);
            Calendar calDriveLeaveTime = Calendar.getInstance();
            calDriveLeaveTime.setTime(driveLeaveTime);
            //获取全天收费-工作日-繁忙时段收费模式charge1,全天收费-工作日-非繁忙时段收费模式charge2，获取全天收费-非工作日-繁忙时段收费模式charge3，获取全天收费-非工作日-非繁忙时段收费模式charge4
            ChargeReturnVo charge1 = new ChargeReturnVo();
            ChargeReturnVo charge2 = new ChargeReturnVo();
            ChargeReturnVo charge3 = new ChargeReturnVo();
            ChargeReturnVo charge4 = new ChargeReturnVo();
            for (ChargeReturnVo charge : chargeReturn) {
                if(charge.getType().equals("0") && charge.getDayType().equals("1") && charge.getHourType().equals("0")){
                    charge1 = charge;
                }
                if(charge.getType().equals("0") && charge.getDayType().equals("1") && charge.getHourType().equals("1")){
                    charge2 = charge;
                }
                if(charge.getType().equals("0") && charge.getDayType().equals("0") && charge.getHourType().equals("0")){
                    charge3 = charge;
                }
                if(charge.getType().equals("0") && charge.getDayType().equals("0") && charge.getHourType().equals("1")){
                    charge4 = charge;
                }
            }
            //判断驶出时间是否为当天 标志符true：当天，false:非当天
            Boolean currentDayFlag = true;
            if(calDriveInTime.get(Calendar.DAY_OF_YEAR) != calDriveLeaveTime.get(Calendar.DAY_OF_YEAR)){
                currentDayFlag = false;
            }
            //判断驶入时间是否为工作日
            if(!(calDriveInTime.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || calDriveInTime.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)){
                //驶入时间为工作日
                //驶出时间为当天
                if(currentDayFlag){
                    //调用方法计算当天收费金额,
                    price1 = calculateDayPrice(charge1, charge2, driveInTimeTime.getTime(), driveLeaveTimeTime.getTime());
                }else {
                //驶出时间不为当天,使用上述方法，将驶离时间设置为24:00 计算首天的金额
                    long leaveTimeTime = 16*3600*1000;
                    price1 = calculateDayPrice(charge1, charge2, driveInTimeTime.getTime(), leaveTimeTime);

                    //计算末天的价格，使用上述方法，将驶入时间设置为00:00:00
                    long inTimeTime = -28800000l;
                    //判断驶出时间为工作日还是非工作日
                    if(!(calDriveLeaveTime.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || calDriveLeaveTime.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)){
                        //驶离时间为工作日
                        price2 = calculateDayPrice(charge1, charge2, inTimeTime, driveLeaveTimeTime.getTime());
                    }else {
                        //驶离时间为非工作日
                        price2 = calculateDayPrice(charge3, charge4, inTimeTime, driveLeaveTimeTime.getTime());
                    }

                    //计算中间天数的收费价格
                    //判断是否有中间天数
                    if((calDriveLeaveTime.get(Calendar.DAY_OF_YEAR) - calDriveInTime.get(Calendar.DAY_OF_YEAR)) >= 2){
                        //有中间天数
                        //计算中间天数有多少工作日，从第二天到倒数第二天
                        int workDayCount = 0;
                        int noWorkDayCount = 0;
                        for (int i = calDriveInTime.get(Calendar.DAY_OF_YEAR) + 1; i < calDriveLeaveTime.get(Calendar.DAY_OF_YEAR); i++){
                            if( ! (i % 7 == 0 || i % 6 == 0)){
                            workDayCount++;
                            }
                        }
                        noWorkDayCount = calDriveLeaveTime.get(Calendar.DAY_OF_YEAR) - calDriveInTime.get(Calendar.DAY_OF_YEAR) - workDayCount - 1;
                        //计算中间天数的收费金额
                        //计算工作日一天的收费金额 驶入时间0:00，驶出时间24:00
                        BigDecimal workOneDayPrice = calculateDayPrice(charge1, charge2, -28800000l, 16 * 3600 * 1000);
                        //计算非工作日一天的收费金额
                        BigDecimal noWorkOneDayPrice = calculateDayPrice(charge3, charge4, -28800000l, 16 * 3600 * 1000);
                        price3 = workOneDayPrice.multiply(new BigDecimal(workDayCount)).add(noWorkOneDayPrice.multiply(new BigDecimal(noWorkDayCount)));

                    }else {
                        //无中间天数
                        //不做处理 price3,默认为0
                    }
                }
            }else {
                //驶入时间为非工作日
                //驶出时间为当天
                if(currentDayFlag){
                    //调用方法计算当天收费金额,
                    price1 = calculateDayPrice(charge3, charge4, driveInTimeTime.getTime(), driveLeaveTimeTime.getTime());
                }else {
                    //驶出时间不为当天,使用上述方法，将驶离时间设置为24:00 计算首天的金额
                    long leaveTimeTime = 16*3600*1000;
                    price1 = calculateDayPrice(charge3, charge4, driveInTimeTime.getTime(), leaveTimeTime);

                    //计算末天的价格，使用上述方法，将驶入时间设置为00:00:00
                    long inTimeTime = -28800000l;
                    //判断驶出时间为工作日还是非工作日
                    if(!(calDriveLeaveTime.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || calDriveLeaveTime.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)){
                        //驶离时间为工作日
                        price2 = calculateDayPrice(charge1, charge2, inTimeTime, driveLeaveTimeTime.getTime());
                    }else {
                        //驶离时间为非工作日
                        price2 = calculateDayPrice(charge3, charge4, inTimeTime, driveLeaveTimeTime.getTime());
                    }

                    //计算中间天数的收费价格
                    //判断是否有中间天数
                    if((calDriveLeaveTime.get(Calendar.DAY_OF_YEAR) - calDriveInTime.get(Calendar.DAY_OF_YEAR)) >= 2){
                        //有中间天数
                        //计算中间天数有多少工作日，从第二天到倒数第二天
                        int workDayCount = 0;
                        int noWorkDayCount = 0;
                        for (int i = calDriveInTime.get(Calendar.DAY_OF_YEAR) + 1; i < calDriveLeaveTime.get(Calendar.DAY_OF_YEAR); i++){
                            if( ! (i % 7 == 0 || i % 6 == 0)){
                                workDayCount++;
                            }
                        }
                        noWorkDayCount = calDriveLeaveTime.get(Calendar.DAY_OF_YEAR) - calDriveInTime.get(Calendar.DAY_OF_YEAR) - workDayCount - 1;
                        //计算中间天数的收费金额
                        //计算工作日一天的收费金额 驶入时间0:00，驶出时间24:00
                        BigDecimal workOneDayPrice = calculateDayPrice(charge1, charge2, -28800000l, 16 * 3600 * 1000);
                        //计算非工作日一天的收费金额
                        BigDecimal noWorkOneDayPrice = calculateDayPrice(charge3, charge4, -28800000l, 16 * 3600 * 1000);
                        price3 = workOneDayPrice.multiply(new BigDecimal(workDayCount)).add(noWorkOneDayPrice.multiply(new BigDecimal(noWorkDayCount)));

                    }else {
                        //无中间天数
                        //不做处理 price3,默认为0
                    }
                }
            }
        }
        //总花费金额为
        BigDecimal totalPrice = price1.add(price2).add(price3);
        return totalPrice;
    }


    /**
     * 根据相关信息计算某一日的收费价格,需传入
     * 两种收费模式：charge1繁忙时段，charge2非繁忙时段
     * 车辆驶入驶出时间：driveInTimeTime,driveLeaveTimeTime,只有时间无日期
     *
     * */
    public BigDecimal calculateDayPrice(ChargeReturnVo charge1,ChargeReturnVo charge2,long driveInTimeTime,long driveLeaveTimeTime){
        driveInTimeTime = driveInTimeTime +28800000l;
        driveLeaveTimeTime = driveLeaveTimeTime + 28800000l;
        //计算停车时间在繁忙时段内的时间
        long minTime = new Date().getTime();
        long maxTime = new Date().getTime();
        //定义收费金额
        BigDecimal priceBusyTime = new BigDecimal(0);
        BigDecimal priceNoBusyTime = new BigDecimal(0);
        //判断两个时间段有无交集,驶入起始时间大于时间段end，或者驶出时间小于时间段起始时间
        if(((driveInTimeTime < (charge1.getHourTimeEnd().getTime() + 28800000l) &&  driveInTimeTime > (charge1.getHourTimeStart().getTime() + 28800000l)) ||
                ((driveLeaveTimeTime > (charge1.getHourTimeStart().getTime() + 28800000l)) && (driveLeaveTimeTime < (charge1.getHourTimeEnd().getTime() + 28800000l))))){
            //时间有交集
            //计算交集时间 minTime
            if(driveInTimeTime > (charge1.getHourTimeStart().getTime() + 28800000l)){
                minTime = driveInTimeTime;
            }else {
                minTime = charge1.getHourTimeStart().getTime() + 28800000l;
            }
            //计算交集时间 maxTime
            if(driveLeaveTimeTime > (charge1.getHourTimeEnd().getTime() + 28800000l)){
                maxTime = charge1.getHourTimeEnd().getTime() + 28800000l;
            }else {
                maxTime = driveLeaveTimeTime;
            }
            //在时间段内的时间为
            long diffTime = maxTime - minTime;
            //计算收费时间，判断是否包含免费时长
            BigDecimal diffTimeHour1 = new BigDecimal(0l);
            if(charge1.getIncludeFree().equals("0")){
                //不包含免费时长
                diffTimeHour1 = new BigDecimal(diffTime/1000/3600);
            }else {
                //包含免费时长,减去免费时长
                diffTimeHour1 = new BigDecimal((diffTime-(charge1.getFreeTime()*60*1000))).divide(new BigDecimal(3600000),BigDecimal.ROUND_CEILING);
            }
            //计算繁忙时段的收费金额
            priceBusyTime = diffTimeHour1.multiply(charge1.getPrice());
            //判断收费金额是否大于封顶金额
            if(priceBusyTime.compareTo(charge1.getMaxMoney()) >= 0){
                //收费金额大于封顶金额
                priceBusyTime = charge1.getMaxMoney();
            }
            //计算非繁忙时段收费时间/h，总泊车时间减去时间段内时间
            //判断是否包含免费时间
            BigDecimal time;
            if(charge2.getIncludeFree().equals("0")){
                //不包含免费时间
                time = new BigDecimal(((driveLeaveTimeTime - driveInTimeTime)-diffTime)/1000/3600);
            }else {
                //包含免费时间
                time = new BigDecimal(((driveLeaveTimeTime - driveInTimeTime)-diffTime-(charge2.getFreeTime()*60*1000))/1000/3600);
            }
            //计算非繁忙时段收费金额
            priceNoBusyTime = time.multiply(charge2.getPrice());
            //判断收费金额是否大于封顶金额
            if(priceNoBusyTime.compareTo(charge2.getMaxMoney()) >= 0){
                //收费金额大于封顶金额
                priceNoBusyTime = charge2.getMaxMoney();
            }

        }else {
            //时间无交集,说明停车时间全在非繁忙时间段
            //计算停车时间
            BigDecimal time;
            //判断是否包含免费时间
            if(charge2.getIncludeFree().equals("0")){
                //不包含免费时间
                time = new BigDecimal((driveLeaveTimeTime - driveInTimeTime)/1000/3600);
            }else {
                //包含免费时间
                time = new BigDecimal((driveLeaveTimeTime - driveInTimeTime-(charge2.getFreeTime()*60*1000))/1000/3600);
            }
            //计算收费金额
            priceNoBusyTime = time.multiply(charge2.getPrice());
            //判断收费金额是否大于封顶金额
            if(priceNoBusyTime.compareTo(charge2.getMaxMoney()) >= 0){
                //收费金额大于封顶金额
                priceNoBusyTime = charge2.getMaxMoney();
            }
        }

        //返回当天收费总金额
        return priceBusyTime.add(priceNoBusyTime);
    }
}
